<template>
	<div class="upload-box">
		<el-upload
			ref="uploadEl"
			action="#"
			:data="formParam.data"
			:id="uuid"
			:style="{ height }"
			:auto-upload="false"
			:class="['upload', self_disabled ? 'disabled' : '']"
			:disabled="self_disabled"
			v-model:file-list="formParam.fileList"
			:show-file-list="false"
			:http-request="handleHttpUpload"
			:on-change="changeFile"
			:on-error="uploadError"
			:on-progress="uploadProgress"
		>
			<template v-if="fileUrl">
				<div class="w-24 h-24">
					<img :src="imgUrl" />
				</div>
				<div class="upload-handle" @click.stop>
					<div class="handle-icon" @click="editFile" v-if="!self_disabled">
						<span class="iconfont iconedit-3" />
						<span>重新上传</span>
					</div>
					<div class="handle-icon" @click="deleteFile" v-if="!self_disabled">
						<span class="iconfont icontrash-2" />
						<span>删除文件</span>
					</div>
				</div>
			</template>
			<template v-else>
				<div class="upload-empty">
					<div v-if="showIcon">
						<i class="iconfont" :class="props.uploadIcon" style="font-size: 35px" />
					</div>
					<div>{{ props.emptyTips }}</div>
				</div>
			</template>
		</el-upload>
		<div v-if="showTips" class="el-upload__tip">
			<slot name="tip"></slot>
		</div>
		<div v-if="showContent" class="text-center">{{ fileName }}</div>
	</div>
</template>

<script setup lang="ts" name="LazyUploadFile">
import { uploadFile } from "@/api/modules/upload";
import defaultImg from "@/assets/images/iot/device/home/7.png";
import { ResultEnum } from "@/enums/httpEnum";
import { generateUUID } from "@/utils/util";
import type { UploadFile, UploadProgressEvent, UploadRequestOptions } from "element-plus";
import { ElNotification, formContextKey, formItemContextKey } from "element-plus";
import { computed, inject, ref } from "vue";
import { showMsg } from "../../utils/util";
interface UploadFileProps {
	emptyTips?: string;
	uploadIcon?: string;
	fileUrl: string; // 文件地址，用于失败文件上传标识
	fileName: string; // 文件名称，用于上传后底部显示文件名
	imgUrl?: string; //背景图片，用于上传文件后的图片装饰
	showTips?: boolean;
	api?: (params: any) => Promise<any>; // 上传文件的 api 方法，一般项目上传都是同一个 api 方法，在组件里直接引入即可 ==> 非必传
	disabled?: boolean; // 是否禁用上传组件 ==> 非必传（默认为 false）
	height?: string; // 组件高度 ==> 非必传（默认为 150px）
	width?: string; // 组件宽度 ==> 非必传（默认为 150px）
	borderRadius?: string; // 组件边框圆角 ==> 非必传（默认为 8px）
	showIcon?: boolean;
	showContent?: boolean;
	title?: string;
}

// 接受父组件参数
const props = withDefaults(defineProps<UploadFileProps>(), {
	title: "上传文件",
	emptyTips: "请选择上传的文件",
	uploadIcon: "iconbx-upload",
	//文件URL
	fileUrl: "",
	fileName: "",
	showTips: true,
	//背景图片
	imgUrl: defaultImg,
	drag: true,
	disabled: false,
	fileSize: 5,
	height: "150px",
	width: "150px",
	borderRadius: "8px",
	showIcon: true,
	showContent: true
});
const uploadEl = ref();
const formParam = reactive<any>({
	fileList: [],
	data: {}
});
const fileInfo = ref();
// 生成组件唯一id
const uuid = ref("id-" + generateUUID());

// 获取 el-form 组件上下文
const formContext = inject(formContextKey, void 0);
// 获取 el-form-item 组件上下文
const formItemContext = inject(formItemContextKey, void 0);
// 判断是否禁用上传和删除
const self_disabled = computed(() => {
	return props.disabled || formContext?.disabled;
});

/**
 * @description 文件上传
 * @param options 上传的文件
 * */
interface UploadEmits {
	(e: "update:fileUrl", value: string): void;
	(e: "update:fileName", value: string): void;
	(e: "success", value: any): void;
	(e: "change", value: any): void;
}
const emit = defineEmits<UploadEmits>();
const handleHttpUpload = async (options: UploadRequestOptions) => {
	let formData = new FormData();
	delete formParam.data.file;
	for (let key in formParam.data) {
		formData.append(key, formParam.data[key]);
	}
	formData.append("file", options.file);
	try {
		const api = props.api ?? uploadFile;
		const res = await api(formData);
		uploadSuccess(res);
		emit("update:fileUrl", res.result?.fileUrl);
		// 调用 el-form 内部的校验方法（可自动校验）
		formItemContext?.prop && formContext?.validateField([formItemContext.prop as string]);
	} catch (error) {
		options.onError(error as any);
	}
};

/**
 * @description 删除文件
 * */
const deleteFile = () => {
	fileInfo.value = null;
	emit("update:fileUrl", "");
};

/**
 * @description 编辑文件
 * */
const editFile = () => {
	const dom = document.querySelector(`#${uuid.value} .el-upload__input`);
	dom && dom.dispatchEvent(new MouseEvent("click"));
};
const changeFile = (file: UploadFile) => {
	fileInfo.value = file;
	emit("update:fileName", file.name);
	emit("update:fileUrl", file.name);
	emit("change", file.name);
};

// 文件上传成功提示
const uploadSuccess = (res: any) => {
	let { code, message } = res;
	emit("success", res);

	//是否需要文件上传提示
	let ok = code == ResultEnum.SUCCESS;
	if (!props.showTips) {
		showMsg(message, ok);
	}
	return ok;
};

// 文件上传错误提示
const uploadError = () => {
	if (!props.showTips) {
		return;
	}
	ElNotification({
		title: "温馨提示",
		message: "文件上传失败，请您重新上传！",
		type: "error"
	});
};
const uploadProgress = (evt: UploadProgressEvent, uploadFile: UploadFile) => {
	console.log(evt);
	console.log(uploadFile);
};
const getFile = () => {
	return fileInfo.value;
	// return formParam.fileList;
};
const submit = (param?: object) => {
	formParam.data = param;
	uploadEl.value.submit();
};
defineExpose({
	getFile,
	submit,
	editFile
});
</script>

<style scoped lang="scss">
.is-error {
	.upload {
		:deep(.el-upload),
		:deep(.el-upload-dragger) {
			border: 1px dashed var(--el-color-danger) !important;
			&:hover {
				border-color: var(--el-color-primary) !important;
			}
		}
	}
}
:deep(.disabled) {
	.el-upload,
	.el-upload-dragger {
		cursor: not-allowed !important;
		background: var(--el-disabled-bg-color);
		border: 1px dashed var(--el-border-color-darker) !important;
		&:hover {
			border: 1px dashed var(--el-border-color-darker) !important;
		}
	}
}
.upload-box {
	.no-border {
		:deep(.el-upload) {
			border: none !important;
		}
	}
	:deep(.upload) {
		.el-upload {
			position: relative;
			display: flex;
			align-items: center;
			justify-content: center;
			width: v-bind(width);
			height: v-bind(height);
			overflow: hidden;
			border: 1px dashed var(--el-border-color-darker);
			border-radius: v-bind(borderRadius);
			transition: var(--el-transition-duration-fast);
			&:hover {
				border-color: var(--el-color-primary);
				.upload-handle {
					opacity: 1;
				}
			}
			.el-upload-dragger {
				display: flex;
				align-items: center;
				justify-content: center;
				width: 100%;
				height: 100%;
				padding: 0;
				overflow: hidden;
				background-color: transparent;
				border: 1px dashed var(--el-border-color-darker);
				border-radius: v-bind(borderRadius);
				&:hover {
					border: 1px dashed var(--el-color-primary);
				}
			}
			.el-upload-dragger.is-dragover {
				background-color: var(--el-color-primary-light-9);
				border: 2px dashed var(--el-color-primary) !important;
			}
			.upload-image {
				width: 100%;
				height: 100%;
				object-fit: contain;
			}
			.upload-empty {
				position: relative;
				display: flex;
				flex-direction: column;
				align-items: center;
				justify-content: center;
				font-size: 12px;
				line-height: 30px;
				color: var(--el-color-info);
				.el-icon {
					font-size: 28px;
					color: var(--el-text-color-secondary);
				}
			}
			.upload-handle {
				position: absolute;
				top: 0;
				right: 0;
				box-sizing: border-box;
				display: flex;
				align-items: center;
				justify-content: center;
				width: 100%;
				height: 100%;
				cursor: pointer;
				background: rgb(0 0 0 / 60%);
				opacity: 0;
				transition: var(--el-transition-duration-fast);
				.handle-icon {
					display: flex;
					flex-direction: column;
					align-items: center;
					justify-content: center;
					padding: 0 6%;
					color: aliceblue;
					.iconfont {
						margin-bottom: 10px;
						font-size: 20px;
					}
					span {
						font-size: 85%;
						line-height: 85%;
					}
				}
			}
		}
	}
	.el-upload__tip {
		line-height: 18px;
		text-align: center;
	}
}
</style>
